/****

    activequant - activestocks.eu

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.


	contact  : contact@activestocks.eu
    homepage : http://www.activestocks.eu

 ****/
package org.activequant.dao.hibernate;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.activequant.core.domainmodel.InstrumentSpecification;
import org.activequant.core.domainmodel.index.IndexComponent;
import org.activequant.core.domainmodel.index.IndexComposition;
import org.activequant.core.types.TimeStamp;
import org.activequant.dao.IIndexCompositionDao;
import org.activequant.util.exceptions.DaoException;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;



/**
 * @TODO Implementation of the IndexComposition class. This enables to group instruments into a index and group indexes into new indexes<br/>
 * IndexCompositionDao extends DaoSupportBase&lt;IndexComposition&gt; implements IIndexCompositionDao.<br/>
 * Holds the following inherited variables:
 * <ul>
 * <li>clazz(Class&lt;IndexComposition&gt;)</li>
 * <li>hibernateTemplate(HibernateTemplate)</li>
 * </ul>
 * <br>
 * <b>History:</b><br>
 *  - [10.08.2008] Created (Wessel de Roode)<br>
 *  
 *  @author Wessel de Roode 
 */
public class IndexCompositionDao extends DaoSupportBase<IndexComposition> implements IIndexCompositionDao  {
	/**
	 * Constructs an IndexCompositionDao(extends DaoSupportBase&lt;IndexComposition&gt; implements IIndexCompositionDao) whose inherited clazz(Class&lt;T&gt;) is set to <code>IndexComposition.class</code>.
	 */
	public IndexCompositionDao() {
		super(IndexComposition.class);
	}
	/**
	 * Constructs an IndexCompositionDao(extends DaoSupportBase&lt;IndexComposition&gt; implements IIndexCompositionDao) whose inherited clazz(Class&lt;T&gt;) is set to <code>IndexComposition.class</code>.<br/>
	 * The given sessionFactory(SessionFactory) is used to set its Hibernate SessionFactory and create/initialize the inherited hibernateTemplate(HibernateTemplate) for it.
	 * @param sessionFactory
	 */
	public IndexCompositionDao(SessionFactory sessionFactory) {		
		super(IndexComposition.class, sessionFactory);
	}
	/**
	 * Returns a List&lt;InstrumentSpecification&gt; of instruments matching the given spec(InstrumentSpecification) (by symbol(Symbol) and vendor(String))
	 * and are involved in an IndexComposition where their activationTimeStamp(TimeStamp) is latest(maximum) but not after the given byThisTime(TimeStamp)
	 */
	@SuppressWarnings("unchecked")
	public List<InstrumentSpecification> findByComponent(InstrumentSpecification spec, TimeStamp byThisTime) {
		
		// TODO Migrate inline code SQL qeuries into the hibernate XML
		String mySQL = "SELECT indx.* "+ 
							" FROM InstrumentSpecification indx,"+
							"     InstrumentSpecification component," +
							"     IndexComponent iComponent," +
							"     (SELECT iComposition.* " +
							"		FROM (SELECT instrumentSpecification_id, MAX(activationTimeStamp) AS max_timestamp" +
							"			   FROM IndexComposition" +
							"				WHERE activationTimeStamp <= :byThisTime GROUP BY instrumentSpecification_id"+
							"     ) AS maxx" +
							"	JOIN IndexComposition iComposition" +
							"		ON iComposition.instrumentSpecification_id = maxx.instrumentSpecification_id" +
							"		   AND iComposition.activationTimeStamp = maxx.max_timestamp) iComposition" +
							" WHERE 	iComponent.instrumentSpecification_id = component.id " +
							"		AND iComponent.indexComposition_id = iComposition.id " +
							"		AND iComposition.InstrumentSpecification_id = indx.id " +
							"		AND component.Symbol = :symbol AND component.Vendor = :vendor ";

		Session sess = getSession();
		
		SQLQuery query = sess.createSQLQuery(mySQL).addEntity("indx", InstrumentSpecification.class);
		List list = query.setString("symbol", spec.getSymbol().toString() ).setString("vendor", spec.getVendor()).setLong("byThisTime", byThisTime.getNanoseconds()).list();
		
		sess.flush();
		sess.close();
		
		if(list.size() == 0){
			return null;
		}
		
		return (List<InstrumentSpecification>)list;
	}
	/**
	 * Returns a List&lt;InstrumentSpecification&gt; of instruments matching the given spec(InstrumentSpecification) (by symbol(Symbol) and vendor(String))
	 * and are involved in an IndexComposition where their activationTimeStamp(TimeStamp) is between the given startTime-endTime(TimeStamp)
	 */
	@SuppressWarnings("unchecked")
	public List<InstrumentSpecification> findByComponentAndTimeInterval(InstrumentSpecification spec, TimeStamp startTime, TimeStamp endTime) {
		
		String mySQL = "SELECT indx.*" +
						" FROM InstrumentSpecification component,InstrumentSpecification indx,"+
						" IndexComponent iComponent, IndexComposition iComposition"+
						"    WHERE iComponent.instrumentSpecification_id = component.id"+
						"          AND iComponent.indexComposition_id = iComposition.id"+ 
						"          AND iComposition.InstrumentSpecification_id = indx.id"+ 
						"          AND component.Symbol = :symbol AND component.Vendor = :vendor"+ 
						"          AND iComposition.activationTimeStamp BETWEEN :startTime AND :endTime "; 
		
		Session sess = getSession();
		
		// TODO Migrate inline code SQL qeuries into the hibernate XML
		SQLQuery query = sess.createSQLQuery(mySQL).addEntity("indx", InstrumentSpecification.class);
		List list = query.setString("symbol", spec.getSymbol().toString() ).setString("vendor", spec.getVendor()).setLong("startTime", startTime.getNanoseconds()).setLong("endTime", endTime.getNanoseconds()).list();		
		sess.flush();
		sess.close();
		if(list.size() == 0){
			return null;
		}
		
		List<InstrumentSpecification> recursiveResult = (List<InstrumentSpecification>)list;
//		Run the tree upwards and recursive
//		for(int i=0; i<list.size(); i++){
//			List<InstrumentSpecification> newLevel = (List<InstrumentSpecification>)findByComponentAndTimeInterval( (InstrumentSpecification)list.get(i), startTime, endTime);
//			if( newLevel != null){
//				recursiveResult.addAll( newLevel );
//			}
//		}
		return recursiveResult;
	}
	/**
	 * Returns the IndexComposition whose components(instruments) match the given spec(InstrumentSpecification) (by symbol(Symbol) and vendor(String))
	 * and whose activationTimeStamp(TimeStamp) is latest(maximum) but not after the given byThisTime(TimeStamp)	
	 */
	@SuppressWarnings("unchecked")
	public IndexComposition findByIndexSpec(InstrumentSpecification spec, TimeStamp byThisTime) {
		String symbol = spec.getSymbol().toString();
		String vendor = spec.getVendor();
		long maxTimeStamp = byThisTime.getNanoseconds();

		// TODO Migrate incode SQL queries into the hibernate QL and one single query instead of two
		// TODO Migrate inline code SQL queries into the hibernate XML
		String mySQL = "SELECT iComposition.* FROM InstrumentSpecification indx, "+
					   " (SELECT instrumentSpecification_id, MAX(activationTimeStamp) AS max_timestamp "+ 
					   "         FROM IndexComposition" +
					   "         WHERE activationTimeStamp <= :byThisTime GROUP BY instrumentSpecification_id"+
					   " ) AS maxx" +
					   " JOIN IndexComposition iComposition" +
					   "      ON iComposition.instrumentSpecification_id = maxx.instrumentSpecification_id" +
					   "         AND iComposition.activationTimeStamp = maxx.max_timestamp"+
					   " WHERE iComposition.instrumentSpecification_id = indx.id"+
					   "       AND indx.Symbol = :symbol AND indx.vendor = :vendor ";
		
		Session sess = getSession();
		SQLQuery query = sess.createSQLQuery(mySQL).addEntity("iComposition", IndexComposition.class);
		List list = query.setString("symbol", symbol).setString("vendor", vendor).setLong("byThisTime", maxTimeStamp).list();
		
		// return result
		if(list.size() == 1) {
			IndexComposition iComposition = (IndexComposition)list.get(0);
			
			// TODO Refractor code, we must be able to do this in one query
			// TODO Migrate inline code SQL queries into the hibernate XML
			mySQL = "SELECT iComponent.*, inst.* FROM IndexComponent iComponent, InstrumentSpecification inst"+
							"  WHERE iComponent.instrumentSpecification_id = inst.id "+
							" AND iComponent.indexComposition_id = :id";

			SQLQuery query2 = sess.createSQLQuery(mySQL).addEntity("iComponent", IndexComponent.class).addEntity("inst", InstrumentSpecification.class);
			List<IndexComponent> list2 = query2.setLong("id",iComposition.getId()).list();
			sess.flush();
			sess.close();
			
			Set componentsSet = new HashSet(list2);
			iComposition.setComponents(componentsSet);
			return iComposition;   
						
		} else if(list.size() > 1) {
			throw new DaoException("Ambigous results");
		}
		return null;
	}
	/**
	 * Returns a List&lt;IndexComposition&gt; whose components(instruments) match the given spec(InstrumentSpecification) (by symbol(Symbol) and vendor(String))
	 * and whose activationTimeStamp(TimeStamp) is between the given startTime-endTime(TimeStamp)	
	 */
	@SuppressWarnings("unchecked")
	public List<IndexComposition> findByIndexSpecAndTimeInterval(InstrumentSpecification spec, TimeStamp startTime, TimeStamp endTime) {

		// TODO Migrate inline code SQL qeuries into the hibernate XML
		String mySQL = "SELECT c1.* FROM IndexComposition c1, InstrumentSpecification i1" +
							" WHERE c1.instrumentSpecification_id = i1.id" +
									" AND i1.Symbol= :symbol"+
									" AND i1.vendor = :vendor"+
									" AND c1.activationTimeStamp BETWEEN :startTime AND :endTime"; 		
		Session sess = getSession();
		SQLQuery query = sess.createSQLQuery(mySQL).addEntity("c1", IndexComposition.class);
		List list = query.setString("symbol", spec.getSymbol().toString()).setString("vendor", spec.getVendor()).setLong("startTime", startTime.getNanoseconds()).setLong("endTime", endTime.getNanoseconds()).list();
		sess.flush();
		sess.close();
				
		if(list.size() == 0){
			return null;
		}
		return (List<IndexComposition>)list;
	}
}
